package znajdowanieKrzywychZamknietych;

import org.apache.log4j.Logger;
import org.apache.log4j.varia.NullAppender;

import typydanych.Obrazek;

public class ZnajdowanieKrzywychZamknietych {
	public static Logger LOG = Logger.getLogger(ZnajdowanieKrzywychZamknietych.class);
	static {
		LOG.addAppender(new NullAppender());
	}
	Obrazek obrazek;
	int pierwszyNiewypalonyX;
	int pierwszyNiewypalonyY;
	int prog = 110;
	TrybWykrywania trybWykrywania;
	UstalaniePozycjiKrzywejZamknietej ustalaniePozycjiKrzywejZamknietej;
	Obrazek wypalonePiksele;

	private boolean czyWypalony(int x, int y) {
		return wypalonePiksele.get(x, y) == RodzajePikseli.NIEWYPALONY ? false : true;
	}

	private void inicjalizacja(Obrazek obrazek) {
		this.obrazek = obrazek;
		this.wypalonePiksele = new Obrazek();
		this.pierwszyNiewypalonyX = -1;
		this.pierwszyNiewypalonyY = -1;
		trybWykrywania = new TrybWykrywania();
		trybWykrywania.ustawTrybPodstawowy();
		ustalaniePozycjiKrzywejZamknietej = new UstalaniePozycjiKrzywejZamknietej();
		ustalaniePozycjiKrzywejZamknietej.getPunktyYObszaru().clear();
		ustalaniePozycjiKrzywejZamknietej.setObszarCyfry(obrazek.getObszarCyfry());
	}

	/**
	 * 
	 * @return true - jeśli wszystkie niewypalone piksele pokrywają się z pikselami tworzącymi cyfrę<br>
	 *         false - jeśli jakieś piksele się nie pokrywają (czyli jest krzywa zamknięta)
	 */
	private boolean porownajNiewypalonePikseleZPikselamiCyfry() {
		for (int y = obrazek.getObszarCyfry().getGora(); y <= obrazek.getObszarCyfry().getDol(); y++) {
			for (int x = 0; x < Obrazek.ROZMIAR; x++) {
				if (!czyWypalony(x, y)) {
					if (!obrazek.czyNieTlo(x, y, prog)) {
						pierwszyNiewypalonyX = x;
						pierwszyNiewypalonyY = y;
						return false;
					}
				}
			}
		}
		return true;
	}

	private boolean wypalPiksel(int x, int y) {
		boolean czyPozaObszaremObrazka = !obrazek.czyWObszarzeObrazka(x, y);

		if (czyPozaObszaremObrazka) {
			return false;
		}
		boolean czyWypalony = czyWypalony(x, y);
		boolean czyNieTlo = obrazek.czyNieTlo(x, y, prog);

		if (czyWypalony || czyNieTlo) {
			return false;
		} else {
			if (trybWykrywania.czyTrybPodstawowy()) {
				wypalonePiksele.set(x, y, RodzajePikseli.WYPALONY);
			} else if (trybWykrywania.czyTrybPierwszegoObszaru()) {
				wypalonePiksele.set(x, y, RodzajePikseli.PIERWSZY_OBSZAR_ZAMKNIETY);
				ustalaniePozycjiKrzywejZamknietej.getPunktyYObszaru().add(y);
			} else {
				wypalonePiksele.set(x, y, RodzajePikseli.DRUGI_OBSZAR_ZAMKNIETY);
				ustalaniePozycjiKrzywejZamknietej.getPunktyYObszaru().add(y);
			}
			return true;
		}

	}

	private boolean zalejPiksel(int x, int y) {
		if (wypalPiksel(x, y)) {
			zalejPiksel(x + 1, y);
			zalejPiksel(x - 1, y);

			zalejPiksel(x, y + 1);
			zalejPiksel(x, y - 1);
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 
	 * @param obrazek
	 *            - obrazek, który należy sprawdzić
	 * @return 0 - jeśli brak krzywych zamkniętych<br>
	 *         1 - jeśli jedna krzywa zamknięta na dole<br>
	 *         2 - jeśli jedna krzywa zamknięta na górze<br>
	 *         3 - jeśli jedna krzywa zamknięta na dole i jedna na górze<br>
	 *         4 - jeśli jedna krzywa zamknięta zajmująca obszar na dole i na górze
	 */
	public int znajdz(Obrazek obrazek) {
		inicjalizacja(obrazek);
		zalejPiksel(0, 0);
		try {
			if (porownajNiewypalonePikseleZPikselamiCyfry()) {
				LOG.info("BRAK KRZYWYCH");
				return 0;
			}
			LOG.info("SA KRZYWE");
			trybWykrywania.ustawTrybPierwszegoObszaru();
			zalejPiksel(pierwszyNiewypalonyX, pierwszyNiewypalonyY);

			if (porownajNiewypalonePikseleZPikselamiCyfry()) {
				int pozycjaKrzywej = ustalaniePozycjiKrzywejZamknietej.sprawdzGdzieJestKrzywaZamknieta();
				return gdzieJestPojedynczaKrzywa(pozycjaKrzywej);
			}
			int pozycjaKrzywej = ustalaniePozycjiKrzywejZamknietej.sprawdzGdzieJestKrzywaZamknieta();
			trybWykrywania.ustawTrybDrugiegoObszaru();
			ustalaniePozycjiKrzywejZamknietej.getPunktyYObszaru().clear();
			zalejPiksel(pierwszyNiewypalonyX, pierwszyNiewypalonyY);
			if (ustalaniePozycjiKrzywejZamknietej.sprawdzGdzieJestKrzywaZamknieta() == pozycjaKrzywej) {
				return gdzieJestPojedynczaKrzywa(pozycjaKrzywej);
			}
			LOG.info("DWIE KRZYWE");
			return 3;
		} finally {
			LOG.info(wypalonePiksele);
		}
	}

	private int gdzieJestPojedynczaKrzywa(int pozycjaKrzywej) {
		LOG.info("JEDNA KRZYWA");
		if (pozycjaKrzywej == UstalaniePozycjiKrzywejZamknietej.KRZYWA_NA_DOLE) {
			LOG.info("KRZYWA NA DOLE");
			return 1;
		} else if (pozycjaKrzywej == UstalaniePozycjiKrzywejZamknietej.KRZYWA_NA_GORZE) {
			LOG.info("KRZYWA NA GORZE");
			return 2;
		}
		LOG.info("KRZYWA NA CALOSCI: " + pozycjaKrzywej);
		return 4;
	}
}
